﻿/*
Copyright (c) 2010 Lance Ward
This software is licensed under the MIT license, except where otherwise noted.
For complete license details, refer to license.txt
*/

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Drawing2D;

namespace ADNColorPicker
{
	public partial class AnglePreview : UserControl
	{
		private ColorBlend gradient = null;
		private Bitmap memBmp = new Bitmap(1, 1);
		private float angle = 0f;
		private bool capturing = false;

		public AnglePreview()
		{
			InitializeComponent();
		}

		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		public ColorBlend Gradient
		{
			get { return gradient; }
			set 
			{ 
				gradient = value;
				DrawGradient();
				this.Invalidate();
			}
		}

		public float Angle
		{
			get { return angle; }
			set
			{
				angle = value;
				DrawGradient();
				this.Invalidate();
			}
		}

		protected override void OnPaint(PaintEventArgs e)
		{
			e.Graphics.DrawImage(memBmp, 0, 0);
			DrawAngle(e.Graphics);
		}

		protected override void OnMouseDown(MouseEventArgs e)
		{
			capturing = true;
			angle = GetAngle(GetCenterPoint(), e.Location);
			DrawGradient();
			this.Invalidate();
		}

		protected override void OnMouseMove(MouseEventArgs e)
		{
			if (capturing)
			{
				angle = GetAngle(GetCenterPoint(), e.Location);
				DrawGradient();
				this.Invalidate();
			}
		}

		protected override void OnMouseUp(MouseEventArgs e)
		{
			capturing = false;
			angle = GetAngle(GetCenterPoint(), e.Location);
			DrawGradient();
			this.Invalidate();
		}

		protected override void OnMouseLeave(EventArgs e)
		{
			capturing = false;
			base.OnMouseLeave(e);
		}

		protected override void OnResize(EventArgs e)
		{
			CreateBitmap(this.Size);
			DrawGradient();
			base.OnResize(e);
		}

		private void CreateBitmap(Size s)
		{
			if (memBmp != null)
			{
				memBmp.Dispose();
				memBmp = null;
			}

			if (s.Height > 0 && s.Width > 0)
			{
				memBmp = new Bitmap(s.Width, s.Height);
			}
		}

		private void DrawGradient()
		{
			if (memBmp != null)
			{
				Graphics g = Graphics.FromImage(memBmp);
				g.Clear(Color.Transparent);

				g.FillRectangle(new HatchBrush(HatchStyle.LargeCheckerBoard, Color.LightGray, Color.DarkGray),
					0, 0, memBmp.Width, memBmp.Height);

				LinearGradientBrush b = new LinearGradientBrush(new Rectangle(0, 0, memBmp.Width, memBmp.Height), Color.Transparent, Color.Transparent, angle); //change to transparent
				if (gradient != null && gradient.Colors.Length > 1) { b.InterpolationColors = gradient; }

				g.FillRectangle(b, new Rectangle(0, 0, memBmp.Width, memBmp.Height));
			}
		}

		private PointF GetCenterPoint()
		{
			return new PointF((float)(memBmp.Width / 2), (float)(memBmp.Height / 2));
		}

		private void DrawAngle(Graphics g)
		{
			Rectangle rect = new Rectangle(0,0, memBmp.Width, memBmp.Height);
			Pen blackPen = new Pen(Color.Black, 2.0f);
			Pen whitePen = new Pen(Color.White, 1.75f);

			blackPen.EndCap = LineCap.ArrowAnchor;
			whitePen.EndCap = LineCap.ArrowAnchor;

			g.CompositingQuality = CompositingQuality.HighQuality;
			g.SmoothingMode = SmoothingMode.AntiAlias;

            PointF ptStart = GetCenterPoint();
            PointF ptEnd = GetAnglePoints(ptStart, GetRectRadius(this.ClientRectangle), angle);

            g.DrawLine(blackPen, ptStart, ptEnd);
            g.DrawLine(whitePen, ptStart, ptEnd);
		}

        //call with a virtual line from the center point of the rectangle
        //to the clicked point to calculate the angle
		private float GetAngle(PointF p1, PointF p2)
		{
			float x = p2.X - p1.X;
			float y = p2.Y - p1.Y;

			float a = (float)(Math.Atan2(y, x) * 180.0 / Math.PI);
			if (a < 0) { a = 270 + (90 - Math.Abs(a)); }

			return a;
		} 

        private PointF GetAnglePoints(PointF center, float radius, float angle)
        {
            //Calculates the X coordinate of the point
			float x = (float)Math.Cos(2 * Math.PI * angle / 360) * radius + center.X;
			//Calculates the Y coordinate of the point
			float y = (float)Math.Sin(2 * Math.PI * angle / 360) * radius + center.Y;
			return new PointF(x, y);
        }

		private float GetRectRadius(Rectangle rect)
		{
			if ((float)(rect.Width / 2) > (float)(rect.Height / 2))
			{ return (float)(rect.Height / 2); }
			else { return (float)(rect.Width / 2); }
		}
	}
}
